#!/usr/bin/env ts-node
const path = require('path')
const fs = require('fs')

if (!process.argv[2]) {
  process.stderr.write(`Usage: ${process.argv[1]} <filename>\n`)
  return -1
}

const filePath = path.join(process.cwd(), process.argv[2])
let outfile
let config
const packageJson = require(path.join(process.cwd(), 'package.json'))
if (packageJson.configSchema) {
  config = packageJson.configSchema
  outfile = "export {}"
} else {
  config = require(filePath).config
  outfile = fs.readFileSync(filePath)
}


const pn = packageJson.name
const output = {
  buffer: [],
  write: function(str) {
    output.buffer.push(str)
  }
}
const indentOne = '  '

output.write(`
// generated by typed-config.js
declare module 'atom' {
  interface ConfigValues {
`)

function writeFlat(pn, config, indentLevel) {
  const indent = indentOne.repeat(indentLevel)
  for (const k of Object.keys(config)) {
    if (config[k].type === 'object') {
      writeFlat(`${pn}.${k}`, config[k].properties, indentLevel)
      writeDeep(`${pn}.${k}`, config[k].properties, indentLevel)
    } else {
      output.write(`${indent}'${pn}.${k}': ${getType(config[k])}\n`)
    }
  }
}
writeFlat(pn, config, 2)

function writeDeep(pn, config, indentLevel) {
  const indent = indentOne.repeat(indentLevel)
  const indent2 = indentOne.repeat(indentLevel + 1)
  output.write(`${indent}'${pn}': {\n`)
  for (const k of Object.keys(config)) {
    if (config[k].type === 'object') {
      writeFlat(k, config[k].properties, indentLevel + 1)
      writeDeep(k, config[k].properties, indentLevel + 1)
    } else {
      output.write(`${indent2}${needQuote(k)}: ${getType(config[k])}\n`)
    }
  }
  output.write(`${indent}}\n`)
}
writeDeep(pn, config, 2)

function needQuote(str) {
  if (str.match(/^[A-Za-z]\w+$/)) return str
  else return `'${str}'`
}

function getType(t, useDefaultRule = true) {
  let type
  if (t.enum) {
    const vals = []
    for (const v of t.enum) {
      if (v.value !== undefined) {
        vals.push(v.value)
      } else {
        vals.push(v)
      }
    }
    type = vals
      .map(x => {
        if (typeof x === 'string') return `'${x}'`
        else if (typeof x.value === 'string') return `'${x.value}'`
        else if (x.value) return x.value
        else return x
      })
      .join(' | ')
    return type
  }
  switch (t.type) {
    case 'string':
      type = 'string'
      break
    case 'integer':
      type = 'number'
      break
    case 'number':
      type = 'number'
      break
    case 'boolean':
      type = 'boolean'
      break
    case 'array':
      if (t.items) {
        const rt = getType(t.items, false)
        if (rt.includes(' ')) type = `Array<${rt}>`
        else type = `${rt}[]`
      } else {
        type = 'any[]'
      }
      break
    case 'color':
      type = 'string'
      break
    case 'object':
      type = 'object'
      break
  }
  if (!type) throw new Error(`Unknown occured: ${JSON.stringify(t)}`)
  if (useDefaultRule && t.default === undefined) type += ' | undefined'
  return type
}

output.write(`\
  }
}
`)

let result = output.buffer.join('')


result = outfile.toString().replace(/(:?\n\/\/ generated by typed-config\.js[^]*)?$/, result)

fs.writeFileSync(filePath, result, {encoding: 'utf8'})
